package cn.jinjimi.app.managers;

import cn.jinjimi.base.Task.TaskExecutor;
import cn.jinjimi.base.preference.PreferenceManager;
import cn.jinjimi.base.util.GsonUtils;
import cn.jinjimi.base.util.StringUtils;
import cn.jinjimi.app.AccountManager;
import cn.jinjimi.app.BuildConfig;
import cn.jinjimi.app.bean.Constants;
import cn.jinjimi.app.controller.CommonController;
import cn.jinjimi.app.controller.event.NoticeEvent.NoticeEvent;
import cn.jinjimi.domain.data.dto.NoticeDto;
import cn.jinjimi.domain.data.dto.NoticeMeMenuDto;
import cn.jinjimi.domain.data.dto.NoticeNewVersionDTO;
import cn.jinjimi.app.entity.NotificationContent;
import cn.jinjimi.app.store.DatabaseManager;
import cn.jinjimi.app.store.dao.NoticeEntityDao;
import cn.jinjimi.app.store.entity.NoticeEntity;
import cn.jinjimi.app.bean.SharePrefKeys;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;

import bolts.Continuation;
import bolts.Task;
import de.greenrobot.dao.DaoException;
import de.greenrobot.dao.query.QueryBuilder;


/**
 * 红点通知管理类
 * Created by Roy on 2016/12/14.
 */
public class NoticeManager {

    /**
     * 根据code返回红点数据
     *
     * @param codes
     * @return
     */
    public static void getNoticesByCode(final String... codes) {

    }

    /**
     * 手动生成一条红点通知
     *
     * @param code
     * @param id
     * @param content
     * @param ssoid
     */
    public static void createNotice(String code, String id, String content, String ssoid) {
        if (code != null && id != null) {
            NoticeEntity entity = new NoticeEntity(id, code, content, System.currentTimeMillis() + Constants.HOUR_1, ssoid);
            NoticeManager.putNotice(entity);
        }
    }

    /* 存储自身生成的红点通知 */
    private static void putNotice(NoticeEntity notice) {
        if (notice != null) {
            try {
                NoticeEntity entity;
                if (notice.ssoid != null) {
                    entity = DatabaseManager.getInstance().getNoticeEntityDao()
                            .queryBuilder()
                            .where(NoticeEntityDao.Properties.NoticeId.eq(notice.noticeId),
                                    NoticeEntityDao.Properties.Ssoid.eq(notice.ssoid))
                            .unique();
                } else {
                    entity = DatabaseManager.getInstance().getNoticeEntityDao()
                            .queryBuilder()
                            .where(NoticeEntityDao.Properties.NoticeId.eq(notice.noticeId),
                                    NoticeEntityDao.Properties.Ssoid.isNull())
                            .unique();
                }
                if (entity == null) {
                    DatabaseManager.getInstance().getNoticeEntityDao().insertOrReplace(notice);
                    getInstance().cacheNotice(notice);
                }

            } catch (DaoException e) {
                // do clear
                DatabaseManager.getInstance().getNoticeEntityDao()
                        .queryBuilder()
                        .where(NoticeEntityDao.Properties.NoticeId.eq(notice.noticeId))
                        .buildDelete();
            }
        }
    }

    /* 判断是否需要更新 */
    private static boolean ifNeedUpdate(String... code) {
        if (code != null && code.length > 0) {  // 查询的code值为null，强制刷新
            long time = PreferenceManager.getLongData(SharePrefKeys.PREF_LAST_REFRESH_NOTICE_TIME);
            List<String> codes = Arrays.asList(code);
            if (codes.contains(NoticeEntity.TypeDrpEvent)) {// 喜报1分钟请求一次
                if (time != 0 && (System.currentTimeMillis() - time) < Constants.MIN_1)
                    return false;
            } else {// 其余5分钟请求一次
                if (time != 0 && (System.currentTimeMillis() - time) < Constants.MIN_1)
                    return false;
            }
        }

        return true;
    }

    private static void updateNoticeFromNet(final String... codes) {
        PreferenceManager.setData(SharePrefKeys.PREF_LAST_REFRESH_NOTICE_TIME, System.currentTimeMillis());
        CommonController.getRedRemindList().continueWith(new Continuation<NoticeDto, Object>() {
            @Override
            public Object then(Task<NoticeDto> task) throws Exception {
                NoticeDto info = task.getResult();
                if (info != null && info.data != null) {
                    //获取成功后记录网络返回的lastModifyTime
                    PreferenceManager.setData(SharePrefKeys.PREF_NOTICE_LAST_MODIFY + AccountManager.getInstance().getAccount().ssoid, info.lastModifyTime);
                    saveOrUpdateNotice(info.data, codes);
                } else { // 网路请求失败直接查询数据库
                    queryNotice(codes);
                }
                return null;
            }
        });
    }

    /* 处理并存储最新红点数据 */
    private static void saveOrUpdateNotice(List<NotificationContent> notificationContents, String... queryCodes) {
        if (notificationContents == null || notificationContents.isEmpty()) {
            queryNotice(queryCodes);
            return;
        }

        if (queryCodes == null || queryCodes.length == 0) {
            queryCodes = NoticeEntity.createAllCodes();
        }

        HashSet<String> setCodes = new HashSet<>();

        for (final NotificationContent content : notificationContents) {
            NoticeEntity noOrigin = DatabaseManager.getInstance().getNoticeEntityDao().queryBuilder()
                    .where(NoticeEntityDao.Properties.NoticeId.eq(content.id))
                    .unique();

            if (noOrigin == null) { // 查询本地没有此条红点记录的情况下存进数据库
                DatabaseManager.getInstance().getNoticeEntityDao().insertInTx(NoticeEntity.createNoticeEntities(content));
            } else {
                noOrigin.updateByNc(content);
                DatabaseManager.getInstance().getNoticeEntityDao().insertOrReplaceInTx(noOrigin);
            }

            setCodes.add(content.code);

        }

        for (String code : queryCodes) {
            setCodes.add(code);
        }

        if (setCodes.contains(NoticeEntity.TypeDrpEvent))
            clearDrpEventNotice();//清理喜报数据保证数据库只有10条最新

        queryNotice((String[]) setCodes.toArray(new String[setCodes.size()]));
    }

    /* 根据code进行数据库查询 */
    private static void queryNotice(String... codes) {
        if (codes == null || codes.length == 0) {
            codes = NoticeEntity.createAllCodes();
        }
        for (String code : codes) {
            List<NoticeEntity> notices = new ArrayList<>();
            QueryBuilder<NoticeEntity> queryBuilder = DatabaseManager.getInstance().getNoticeEntityDao().queryBuilder()
                    .where(NoticeEntityDao.Properties.Code.eq(code))
                    .whereOr(NoticeEntityDao.Properties.Ssoid.isNull(), NoticeEntityDao.Properties.Ssoid.eq(AccountManager.getInstance().getAccount().ssoid))
                    .where(NoticeEntityDao.Properties.HadShowed.eq(false))
                    .where(NoticeEntityDao.Properties.ExpirTime.ge(System.currentTimeMillis()))
                    .orderDesc(NoticeEntityDao.Properties.StartTime);
            notices.addAll(queryBuilder.list());
            NoticeEvent.dispatchNotice(notices, code);
            getInstance().cacheNotices(code, notices);
        }
    }

    /**
     * 根据code来修改本地数据库红点数据状态
     */
    public static void clickNoticeByCode(String code) {
        Task.forResult(code).continueWith(new Continuation<String, Void>() {
            @Override
            public Void then(Task<String> task) throws Exception {
                List<NoticeEntity> entities = DatabaseManager.getInstance().getNoticeEntityDao().queryBuilder()
                        .where(NoticeEntityDao.Properties.Code.eq(task.getResult()))
                        .list();
                clearNoticeShownState(entities, false);
                NoticeEvent.dispatchClickNotice(task.getResult());
                return null;
            }
        }, Task.BACKGROUND_EXECUTOR);

    }

    /**
     * 根据id来修改本地数据库红点数据状态
     */
    public static void clickNoticeByIds(String... ids) {

        getInstance().readNoticeByIds(ids);

        Task.forResult(Arrays.asList(ids)).continueWith(new Continuation<List<String>, Void>() {
            @Override
            public Void then(Task<List<String>> task) throws Exception {

                List<NoticeEntity> entities = DatabaseManager.getInstance().getNoticeEntityDao().queryBuilder()
                        .where(NoticeEntityDao.Properties.NoticeId.in(task.getResult()))
                        .list();
                clearNoticeShownState(entities, true);
                return null;
            }
        }, Task.BACKGROUND_EXECUTOR);
    }

    /**
     * 根据id来修改本地数据库红点数据状态
     */
    public static void clickNoticeById(String id) {

        getInstance().readNoticeById(id);

        Task.forResult(id).continueWith(new Continuation<String, Void>() {
            @Override
            public Void then(Task<String> task) throws Exception {
                List<NoticeEntity> entities = DatabaseManager.getInstance().getNoticeEntityDao().queryBuilder()
                        .where(NoticeEntityDao.Properties.NoticeId.eq(task.getResult()))
                        .list();
                clearNoticeShownState(entities, true);
                return null;
            }
        }, Task.BACKGROUND_EXECUTOR);
    }

    /* 标记红点为已显示过 */
    private static void clearNoticeShownState(List<NoticeEntity> entities, boolean hasDispatcher) {

        if (entities != null && entities.size() > 0) {
            String ssoid = AccountManager.getInstance().isLogin() ? AccountManager.getInstance().getAccount().ssoid : null;
            for (NoticeEntity e : entities) {
                if ((e.ssoid == null) || (ssoid != null && ssoid.equals(e.ssoid))) {
                    e.hadShowed = true;
                    DatabaseManager.getInstance().getNoticeEntityDao().insertOrReplace(e);
                }
                if (hasDispatcher)
                    NoticeEvent.dispatchClickNotice(e.code);
            }
        }
    }

    /**
     * 检查并删除无用数据
     */
    public static void clearNotice() {
        Task.callInBackground(new Callable<Void>() {
            @Override
            public Void call() throws Exception {

                // 清理过期数据
                DatabaseManager.getInstance().getNoticeEntityDao().queryBuilder()
                        .where(NoticeEntityDao.Properties.ExpirTime.le(System.currentTimeMillis()))
                        .where(NoticeEntityDao.Properties.Code.notEq(NoticeEntity.TypeActivityInfo))
                        .buildDelete().executeDeleteWithoutDetachingEntities();

                // newVersion versionCode不符合数据
                List<NoticeEntity> noticeNewVersion = DatabaseManager.getInstance().getNoticeEntityDao().queryBuilder()
                        .where(NoticeEntityDao.Properties.Code.eq(NoticeEntity.TypeNewVersion)).list();
                for (int i = noticeNewVersion.size() - 1; i > -1; i--) {
                    NoticeNewVersionDTO dtoVersion = GsonUtils.fromJson(noticeNewVersion.get(i).content, NoticeNewVersionDTO.class);
                    if (dtoVersion == null || dtoVersion.versionCode <= BuildConfig.VERSION_CODE)
                        DatabaseManager.getInstance().getNoticeEntityDao().deleteInTx(noticeNewVersion.get(i));
                }

                // newFeature name为null的数据
                List<NoticeEntity> noticeMeMenu = DatabaseManager.getInstance().getNoticeEntityDao().queryBuilder()
                        .where(NoticeEntityDao.Properties.Code.eq(NoticeEntity.TypeNewFeature)).list();
                for (int i = noticeMeMenu.size() - 1; i > -1; i--) {
                    NoticeMeMenuDto dtoMeMenu = GsonUtils.fromJson(noticeMeMenu.get(i).content, NoticeMeMenuDto.class);
                    if (dtoMeMenu == null || StringUtils.isEmpty(dtoMeMenu.title))
                        DatabaseManager.getInstance().getNoticeEntityDao().deleteInTx(noticeMeMenu.get(i));
                }

                return null;
            }
        });
    }

    /* 清理喜报数据保证数据库只有10条最新数据 */
    private static void clearDrpEventNotice() {
        List<NoticeEntity> noticeEntities = DatabaseManager.getInstance().getNoticeEntityDao().queryBuilder()
                .where(NoticeEntityDao.Properties.Code.eq(NoticeEntity.TypeDrpEvent))
                .orderDesc(NoticeEntityDao.Properties.StartTime)
                .list();
        if (noticeEntities.size() > 10) {
            noticeEntities = noticeEntities.subList(10, noticeEntities.size());
            DatabaseManager.getInstance().getNoticeEntityDao().deleteInTx(noticeEntities);
        }
    }

    /**
     * 判断是否需要显示公司的红点
     *
     * @return
     */
    public static boolean hasSetCompany() {
        return true;
    }

    private static volatile NoticeManager instance = null;

    public static NoticeManager getInstance() {
        if (instance == null) {
            synchronized (NoticeManager.class) {
                if (instance == null) {
                    instance = new NoticeManager();
                }
            }
        }

        return instance;
    }

    private Map<String, List<NoticeEntity>> notices = new HashMap<>();

    private void cacheNotices(String code, List<NoticeEntity> list) {
        notices.put(code, list);
    }


    private void cacheNotice(NoticeEntity notice) {

        List<NoticeEntity> notices = getCachedNotices(notice.code);

        if (notices == null) {
            notices = new ArrayList<>();
            notices.add(notice);
            cacheNotices(notice.code, notices);
        } else {
            notices.add(notice);
        }

        NoticeEvent.dispatchNotice(notices, notice.code);
    }

    private List<NoticeEntity> getCachedNotices(String code) {
        return notices.get(code);
    }


    private List<NoticeMeMenuDto> matchedForNewFeature = new ArrayList<>();

    public boolean hasNewFeature() {
        return matchedForNewFeature.size() > 0;
    }


    public boolean hasNewFeature(String title) {
        for (NoticeMeMenuDto dto : matchedForNewFeature) {
            if (dto.title.equals(title)) {
                return true;
            }
        }
        return false;
    }

    public void readNewFeature(String title) {
        List<NoticeMeMenuDto> toRemove = new ArrayList<>();
        for (NoticeMeMenuDto dto : matchedForNewFeature) {
            if (dto.title.equals(title)) {
                clickNoticeById(dto.noticeId);
                toRemove.add(dto);
            }
        }
        //lock the list
        synchronized (matchedForNewFeature) {
            matchedForNewFeature.removeAll(toRemove);
        }
    }

    public List<NoticeMeMenuDto> checkNewFeatures(String... titles) {
        return checkNewFeatures(Arrays.asList(titles));
    }

    public List<NoticeMeMenuDto> checkNewFeatures(List<String> titles) {
        matchedForNewFeature.clear();
        List<NoticeEntity> list = getCachedNotices(NoticeEntity.TypeNewFeature);
        if (list != null && list.size() > 0 && titles != null && !titles.isEmpty()) {
            for (String title : titles) {
                for (NoticeMeMenuDto dto : NoticeEntity.getMeNotices(list)) {
                    if (dto.title.equals(title)) {
                        matchedForNewFeature.add(dto);
                    }
                }
            }
        }
        return matchedForNewFeature;
    }


    public void readNoticeById(String id) {
        readNoticeByIds(id);
    }

    public void readNoticeByIds(String... ids) {

        if (ids == null || ids.length == 0)
            return;

        List<String> toRemoveIds = Arrays.asList(ids);

        for (List<NoticeEntity> list : notices.values()) {
            List<NoticeEntity> toRemove = new ArrayList<>();
            for (NoticeEntity entity : list) {
                if (toRemoveIds.contains(entity.noticeId)) {
                    toRemove.add(entity);
                }
            }
            if (toRemove.size() > 0)
                list.removeAll(toRemove);
        }
    }

    public NoticeEntity getNoticeById(String id) {
        for (List<NoticeEntity> list : notices.values()) {
            for (NoticeEntity entity : list) {
                if (entity.noticeId != null && entity.noticeId.equals(id)) {
                    return entity;
                }
            }
        }
        return null;
    }

}